﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Drawing.Printing;
using System.IO;
using CrystalDecisions.CrystalReports.Engine;
using CrystalDecisions.Shared;

namespace E4RPPrintManager
{
    public class PrintManager
    {
        private DataSet _dataSource;
        private string _reportFile;
        private readonly PrinterSettings _printerSettings;
        private readonly PageSettings _pageSettings;
        private ReportDocument _reportDocument;
        private bool _dataSourcePushed = true;
        private readonly Hashtable _parameters;
        private PaperOrientation _paperOrientation = PaperOrientation.DefaultPaperOrientation;
        private bool _allowMultipleCopies;

        public PrintManager()
        {
            _printerSettings = new PrinterSettings();
            _pageSettings = _printerSettings.DefaultPageSettings;
            _pageSettings.Margins.Top = 30;
            _pageSettings.Margins.Left = 30;
            _pageSettings.Margins.Bottom = 30;
            _pageSettings.Margins.Right = 30;
            _parameters = new Hashtable();
        }

        #region Private Methods

        private void SetReportDocument()
        {
            if (_reportDocument == null)
            {
                _reportDocument = new ReportDocument();
                _reportDocument.Load(_reportFile);
            }
            if (!_dataSourcePushed)
            {
                _reportDocument.SetDataSource(_dataSource);
                _dataSourcePushed = true;
                ResetParameters();
                foreach (ReportDocument subReport in _reportDocument.Subreports)
                {
                    subReport.SetDataSource(_dataSource);
                    _dataSourcePushed = true;
                    ResetParameters();
                }
            }
            try
            {
                _pageSettings.Landscape = _paperOrientation == PaperOrientation.Landscape;                
                _reportDocument.PrintOptions.CopyFrom(_printerSettings, _pageSettings);
            }
            catch (Exception)
            {
                //throw new Exception("Print generate exception:" + e.Message);
            }
        }

        private void SetCopyNumberParameter(int copyNumber)
        {
            SetParameterField("CopyNumber", copyNumber);
        }

        private void SetCopyNameParameter(string copyName)
        {
            SetParameterField("CopyName", copyName);
        }

        private void SetCopyNumber(int copyNumber)
        {
            SetParameters(copyNumber, GetCopyName(copyNumber));
        }

        private void SetParameters(int copyNumber, string copyName)
        {
            SetCopyNumberParameter(copyNumber);
            SetCopyNameParameter(copyName);
        }

        private void ResetParameters()
        {
            SetParameters(0, String.Empty);
        }

        private static string GetCopyName(int copyNumber)
        {
            switch (copyNumber)
            {
                case 1:
                    return "Original";
                case 2:
                    return "Duplicado";
                case 3:
                    return "Triplicado";
                default:
                    return String.Empty;
            }
        }

        private bool CanPrintMultipleCopies()
        {
            return _allowMultipleCopies && _printerSettings.Copies > 1 && (_parameters.Count == 0 || GetParameterField("CopyName") != null);
        }

        private void SetParameterField(string fieldName, object fieldValue)
        {
            IParameterField parameterField = GetParameterField(fieldName);
            if (parameterField == null)
                return;
            parameterField.CurrentValues.Clear();
            parameterField.CurrentValues.AddValue(fieldValue);
        }

        private IParameterField GetParameterField(string fieldName)
        {
            if (_parameters.ContainsKey(fieldName))
                return (IParameterField)_parameters[fieldName];
            IParameterField parameterField = _reportDocument.ParameterFields[fieldName];
            if (parameterField == null)
                return null;
            _parameters.Add(fieldName, parameterField);
            return parameterField;
        }

        private void PrintMultipleCopies()
        {
            short copies = _printerSettings.Copies;
            for (int copyNumber = 1; copyNumber <= copies; copyNumber++)
            {
                SetCopyNumber(copyNumber);
                _reportDocument.PrintToPrinter(1, _printerSettings.Collate, _printerSettings.FromPage, _printerSettings.ToPage);
                _printerSettings.Copies--;
            }
            ResetParameters();
        }

        private void PrintMultipleCopies(PrintManagerParameter[] parameters)
        {
            short copies = _printerSettings.Copies;
            for (int copyNumber = 1; copyNumber <= copies; copyNumber++)
            {
                SetCopyNumber(copyNumber);
                if (parameters.Length > 0) SetParameters(parameters);
                _reportDocument.PrintToPrinter(1, _printerSettings.Collate, _printerSettings.FromPage, _printerSettings.ToPage);
                _printerSettings.Copies--;
            }
            ResetParameters();
        }

        private void SetParameters(IEnumerable<PrintManagerParameter> parameters)
        {
            foreach (var parameter in parameters)
                SetParameterField(parameter.FieldName, parameter.FieldValue);
        }

        private void InitParameters()
        {
            var val = new ParameterDiscreteValue {Value = ""};
            if (_reportDocument.ParameterFields.Count == 0)
                return;
            foreach (ParameterField parameter in _reportDocument.ParameterFields)
                if (parameter.CurrentValues.Count == 0)
                    parameter.CurrentValues.Add(val);
        }

        private static void LogonToReport(ReportDocument reportDocument, string server, string database, string id, string password)
        {
            foreach (Table table in reportDocument.Database.Tables)
            {
                TableLogOnInfo logonInfo = table.LogOnInfo;
                logonInfo.ConnectionInfo.ServerName = server;
                logonInfo.ConnectionInfo.DatabaseName = database;
                logonInfo.ConnectionInfo.UserID = id;
                logonInfo.ConnectionInfo.Password = password;
                table.ApplyLogOnInfo(logonInfo);
            }
        }

        #endregion Private Methods

        #region Public Properties

        public DataSet DataSource
        {
            set
            {
                Debug.Assert(value != null, "Report data source can't be null.");
                Debug.Assert(value.Tables[0] != null, "Report data source must be filled.");
                Debug.Assert(value.Tables[0].Rows.Count > 0, "Must contain at least a row.");
                _dataSource = value;
                _dataSourcePushed = false;
            }
        }

        public string ReportFile
        {
            set
            {
                if (!(new FileInfo(value)).Exists)
                    throw new Exception("Report file must be specified.");
                _reportFile = value;
                _dataSourcePushed = false;
                _parameters.Clear();
            }
        }

        public string PrinterName
        {
            set
            {
                _printerSettings.PrinterName = value;
                if (!_printerSettings.IsValid)
                    throw new Exception("The printer name " + value + "is not valid");
            }
        }

        public bool AllowMultipleCopies
        {
            set { _allowMultipleCopies = value; }
        }

        public short NumberOfCopies
        {
            set
            {
                _printerSettings.Copies = value;
                _allowMultipleCopies = _printerSettings.Copies > 1;
            }
        }

        public PaperOrientation PaperOrientation
        {
            set { _paperOrientation = value; }
        }

        #endregion Public Properties

        public void Print()
        {
            Debug.Assert(_dataSourcePushed || _dataSource != null, "Report data source can't be null, if not yet pushed to the report");
            Debug.Assert(_reportFile != String.Empty, "Report file must be specified.");
            Debug.Assert(_printerSettings != null, "The printer settings must be specified.");

            SetReportDocument();
            InitParameters();
            if (_allowMultipleCopies && CanPrintMultipleCopies())
                PrintMultipleCopies();
            else
                _printerSettings.Copies = 1;
            if (_printerSettings.Copies == 0)
                return;
            _reportDocument.PrintToPrinter(_printerSettings.Copies, _printerSettings.Collate, _printerSettings.FromPage, _printerSettings.ToPage);
        }

        public void PrintDatabaseLogon(string user, string password, string server, string database)
        {
            Debug.Assert(_reportFile != String.Empty, "Report file must be specified.");
            Debug.Assert(_printerSettings != null, "The printer settings must be specified.");

            if (_reportDocument == null)
            {
                _reportDocument = new ReportDocument();
                _reportDocument.Load(_reportFile);
            }
            LogonToReport(_reportDocument, server, database, user, password);
            ResetParameters();
            foreach (ReportDocument subReport in _reportDocument.Subreports)
            {
                LogonToReport(subReport, server, database, user, password);
                ResetParameters();
            }
            try
            {
                _pageSettings.Landscape = _paperOrientation == PaperOrientation.Landscape;
                _reportDocument.PrintOptions.CopyFrom(_printerSettings, _pageSettings);
            }
            catch (Exception)
            {
                //throw new Exception("Print generate exception:" + e.Message);
            }
            if (_allowMultipleCopies && CanPrintMultipleCopies())
                PrintMultipleCopies();
            else
                _printerSettings.Copies = 1;
            if (_printerSettings.Copies == 0)
                return;
            _reportDocument.PrintToPrinter(_printerSettings.Copies, _printerSettings.Collate, _printerSettings.FromPage, _printerSettings.ToPage);
        }

        public void PrintPreviewDatabaseLogon(string user, string password, string server, string database)
        {
            Debug.Assert(_reportFile != String.Empty, "Report file must be specified.");
            Debug.Assert(_printerSettings != null, "The printer settings must be specified.");

            if (_reportDocument == null)
            {
                _reportDocument = new ReportDocument();
                _reportDocument.Load(_reportFile);
            }
            LogonToReport(_reportDocument, server, database, user, password);
            ResetParameters();
            foreach (ReportDocument subReport in _reportDocument.Subreports)
            {
                LogonToReport(subReport, server, database, user, password);
                ResetParameters();
            }
            var form = new PrintPreviewXtraForm();
            form.Setup(_reportDocument);
            form.ShowDialog();
        }

        public string GetDefaultPrinterName()
        {
            var printDocument = new PrintDocument();
            return printDocument.PrinterSettings.PrinterName;
        }

        public void PrintDatabaseLogon(string user, string password, string server, string database, params PrintManagerParameter[] parameters)
        {
            Debug.Assert(_reportFile != String.Empty, "Report file must be specified.");
            Debug.Assert(_printerSettings != null, "The printer settings must be specified.");

            if (_reportDocument == null)
            {
                _reportDocument = new ReportDocument();
                _reportDocument.Load(_reportFile);
            }
            LogonToReport(_reportDocument, server, database, user, password);
            ResetParameters();
            foreach (ReportDocument subReport in _reportDocument.Subreports)
            {
                LogonToReport(subReport, server, database, user, password);
                ResetParameters();
            }
            try
            {
                _pageSettings.Landscape = _paperOrientation == PaperOrientation.Landscape;
                _reportDocument.PrintOptions.CopyFrom(_printerSettings, _pageSettings);
            }
            catch (Exception)
            {
                //throw new Exception("Print generate exception:" + e.Message);
            }
            if (_allowMultipleCopies && CanPrintMultipleCopies())
                PrintMultipleCopies(parameters);
            else
                _printerSettings.Copies = 1;
            if (_printerSettings.Copies == 0)
                return;
            if (parameters.Length > 0) SetParameters(parameters);
            _reportDocument.PrintToPrinter(_printerSettings.Copies, _printerSettings.Collate, _printerSettings.FromPage, _printerSettings.ToPage);
        }

        public void PrintPreviewDatabaseLogon(string user, string password, string server, string database, params PrintManagerParameter[] parameters)
        {
            Debug.Assert(_reportFile != String.Empty, "Report file must be specified.");
            Debug.Assert(_printerSettings != null, "The printer settings must be specified.");

            if (_reportDocument == null)
            {
                _reportDocument = new ReportDocument();
                _reportDocument.Load(_reportFile);
            }
            LogonToReport(_reportDocument, server, database, user, password);
            ResetParameters();
            foreach (ReportDocument subReport in _reportDocument.Subreports)
            {
                LogonToReport(subReport, server, database, user, password);
                ResetParameters();
            }            
            if (parameters.Length > 0) SetParameters(parameters);
            var form = new PrintPreviewXtraForm();
            form.Setup(_reportDocument);
            form.ShowDialog();
        }
        
    }
}